package com.finance.framework.controller;

import java.io.File;
import java.util.*;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;

import com.finance.base.constant.BaseConstants;
import com.finance.base.exception.BaseException;
import com.finance.system.bean.pojo.User;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class BaseController implements ApplicationContextAware{
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    private ApplicationContext applicationContext;

    @Autowired
    private AuthorizationFilter authorizationFilter;

    @ExceptionHandler(Exception.class)
    public ModelAndView handleException(Exception ex, HttpServletRequest request) {
        if (ex instanceof BaseException) {
            BaseException baseException = (BaseException) ex;
            logger.error("业务异常", ex);
            return this.getFailResult(baseException.getErrorCode(),baseException.getErrorMsg()).getModelAndView();
        }else if(ex instanceof BindException) {
            BindException bindException = (BindException)ex;
            BindingResult bindingResult = bindException.getBindingResult();
            List<FieldError> errors = bindingResult.getFieldErrors();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(BaseConstants.getErrorMessage(BaseConstants.ERR_INVALID_PARAMETER));
            for (FieldError err:errors) {
                stringBuilder.append(err.getField());
                stringBuilder.append(":");
                stringBuilder.append(err.getDefaultMessage());
                stringBuilder.append("||");
            }
            stringBuilder.replace(stringBuilder.lastIndexOf("||"),stringBuilder.length(),"");
            logger.error(stringBuilder.toString());
            return this.getFailResult(BaseConstants.ERR_INVALID_PARAMETER,stringBuilder.toString()).getModelAndView();
         }else if(ex instanceof AccountException){
            String error = ex.getMessage();
            logger.error(error);
            return this.getFailResult(BaseConstants.ERR_UNKONW_USER,error).getModelAndView();
        }
         else if(ex instanceof AuthenticationException){
            String error = ex.getMessage();
            logger.error(error);
            return this.getFailResult(BaseConstants.ERR_UNKONW_USER,error).getModelAndView();
        }
        else {
            logger.error("程序错误", ex.getMessage());
        }
        return this.getFailResult(BaseConstants.ERR_UNKOWN,BaseConstants.getErrorMessage(BaseConstants.ERR_UNKOWN)).getModelAndView();
    }

    /**
     * 默认的访问地址
     * @param url
     * @return
     */
    @RequestMapping("{url}")
    public ModelAndView defaultMapping(@PathVariable String url){
        Class clasz = this.getClass();
        RequestMapping requestMapping = (RequestMapping)clasz.getAnnotation(RequestMapping.class);
        if(requestMapping==null){
            return new ModelAndView(url);
        }else{
            String[] mapping = requestMapping.value();
            String prefix = StringUtils.isBlank(mapping[0])? "" : mapping[0].endsWith("/")? mapping[0] : mapping[0]+"/";
            url = prefix+url;
        }
        return new ModelAndView(url);

    }

    protected Map<String, Object> getResult(String errorCode, String errorMsg, Object obj) {
        Map<String, Object> jsonMap = new HashMap<String, Object>();
        jsonMap.put("code", errorCode);
        jsonMap.put("msg", errorMsg);
        jsonMap.put("data", obj);
        return jsonMap;
    }


    public Result getSuccessResult(){
        Map<String,Object> object = this.getResult( BaseConstants.ERR_SUCCESS, BaseConstants.getErrorMessage(BaseConstants.ERR_SUCCESS), Collections.EMPTY_MAP);
        return new Result(object);
    }

    public Result getSuccessResult(Object obj){
        Map<String,Object> object = this.getResult( BaseConstants.ERR_SUCCESS, BaseConstants.getErrorMessage(BaseConstants.ERR_SUCCESS), obj);
        WebApplicationContext context = null;
        return new Result(object);
    }

    public Result getFailResult(String errorCode,String errorMsg){
        Map<String,Object> object = this.getResult( errorCode, errorMsg, Collections.EMPTY_MAP);
        return new Result(object);
    }

    protected String getRealPath(String path) {
        ServletContext app = getSessionContext().getServletContext();
        if (null != app) {
            String root = app.getRealPath(String.valueOf(File.separatorChar));
            return root + path;
        }
        return path;
    }

    protected HttpSession getSessionContext() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession(false);
    }

    public Map<String, String> getRequestMap(HttpServletRequest request) {
        Map<String, String> resultMap = new HashMap<String, String>();
        Map<?, ?> requestMap = request.getParameterMap();
        for (Object key : requestMap.keySet()) {
            resultMap.put(String.valueOf(key), request.getParameter(String.valueOf(key)) == null ? "" : request.getParameter(String.valueOf(key)).trim());
        }
        return resultMap;
    }

    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-REAL-IP");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
    }
    public ApplicationContext getApplicationContext(){
        return this.applicationContext;
    }
    protected User getAuthUser(){
        Subject currentUser = SecurityUtils.getSubject();
        User user = (User)currentUser.getSession().getAttribute("authUser");
        return user;
    }

    /**
     * 验证参数
     * */
    private static ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    public static <T> List<String> validate(T t) {
        Validator validator = factory.getValidator();
        Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
        List<String> messageList = new ArrayList<>();
        for (ConstraintViolation<T> constraintViolation : constraintViolations) {
            messageList.add(constraintViolation.getMessage());
        }
        return messageList;
    }
}
